package com.yxkj.sms;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Environment;
import android.os.Process;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * UncaughtExceptionHandler：线程未捕获异常控制器是用来处理未捕获异常的。 如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
 * 实现该接口并注册为程序中的默认未捕获异常处理 这样当未捕获异常发生时，就可以做些异常处理操作 例如：收集异常信息，发送错误报告 等。
 * <p>
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 */
public class CrashHandler implements UncaughtExceptionHandler {

    /**
     * Debug Log Tag
     */
    public static final String TAG = "CrashHandler";

    /**
     * CrashHandler实例
     */
    private static CrashHandler INSTANCE = new CrashHandler();

    /**
     * 是否开启日志输出, 在Debug状态下开启, 在Release状态下关闭以提升程序性能
     */
    private boolean DEBUG = true;

    /**
     * 程序的Context对象
     */
    private Context mContext;

    /**
     * 系统默认的UncaughtException处理类
     */
    private UncaughtExceptionHandler mDefaultHandler;

    // 用来存储设备信息和异常信�?
    private Map<String, String> infos = new HashMap<String, String>();

    /**
     * 错误报告文件的扩展名
     */
    private static final String CRASH_REPORTER_EXTENSION = ".txt";

    /**
     * 用于格式化日期?,作为日志文件名的一部分
     */
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

    /**
     * 保证只有一个CrashHandler实例
     */
    private CrashHandler() {

    }

    /**
     * 获取 CrashHandler 实例 ,单例模式
     */
    public static CrashHandler getInstance() {
        if (INSTANCE == null)
            INSTANCE = new CrashHandler();
        return INSTANCE;
    }

    /**
     * 初始�?
     *
     * @param context
     */
    public void init(Context context) {
        mContext = context;
        // 获取系统默认的UncaughtException 处理器?
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该 CrashHandler 为程序的默认处理器?
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException 发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {

        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
//            try {
//                // Sleep一会后结束程序,来让线程停止一会是为了显示Toast信息给用户，然后Kill程序
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//               Log.e(TAG, "error : " ,e);
//            }
            // 退出应用程序?
            Process.killProcess(Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 自定义错误处理，收集错误信息，发送错误报告等操作均在此完�?
     *
     * @param ex
     * @return true：如果处理了该异常信息；否则返回 false
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
            //在控制台打印崩溃信息
            logExceptionStackTrace(ex);
            //保存崩溃信息到sd卡
            saveCrashInfo(ex);
            // 保存日志文件
//            saveCrashInfo2File(ex);
//        restartApplicationDelay();
        return true;
    }

    private void logExceptionStackTrace(Throwable ex) {
        StringWriter writer = new StringWriter();
        ex.printStackTrace(new PrintWriter(writer));
        Log.e("17phone","  17崩溃日志 "+writer.toString());
    }

    private boolean saveCrashInfo(Throwable exception) {
        if (exception == null) {
            return false;
        }
        File crashFile = getCrashFile();
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(crashFile);
            exception.printStackTrace(pw);
            pw.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } finally {
            if (pw != null) {
                pw.close();
            }
        }
        return true;
    }

    private File getCrashFile() {
        String crashFileName = "crash-" + formatter.format(new Date()) + CRASH_REPORTER_EXTENSION;
        File crashFile = null;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//			crashFile = new File(mContext.getExternalFilesDir("Crash"), crashFileName);
            crashFile = new File(CommonConfig.CRASH, crashFileName);
        } else {
            crashFile = new File(mContext.getFilesDir(), "Crash");
            if (!crashFile.exists()) {
                crashFile.mkdirs();
            }
            crashFile = new File(crashFile, crashFileName);
        }
        return crashFile;
    }

    /**
     * 重启应用，并且退出当前异常
     */
    private void restartApplicationDelay() {
        Intent intent = new Intent();
        // 参数1：包名，参数2：程序入口的activity
        intent.setClassName(mContext.getPackageName(), "SplashActivity");
        PendingIntent restartIntent = PendingIntent.getActivity(mContext.getApplicationContext(), -1, intent, Intent.FLAG_ACTIVITY_NEW_TASK);
        // 1秒钟后重启应用
        AlarmManager mgr = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 10, restartIntent);
        // 关闭当前应用
    }//

    private void closeCurrentApplication() {
        Process.killProcess(Process.myPid());
        System.exit(0);
    }

    /**
     * 保存错误信息到文件中 *
     *
     * @param ex
     * @return 返回文件名称, 便于将文件传送到服务端
     */
    private String saveCrashInfo2File(Throwable ex) {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();

        String result = writer.toString();
        sb.append(result);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";

            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = CommonConfig.CRASH;
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
            }

            return fileName;
        } catch (Exception e) {
        }
        return null;
    }

}